package com.atguigu.daijia.coupon.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.coupon.mapper.CouponInfoMapper;
import com.atguigu.daijia.coupon.mapper.CustomerCouponMapper;
import com.atguigu.daijia.coupon.service.CouponInfoService;
import com.atguigu.daijia.model.entity.coupon.CouponInfo;
import com.atguigu.daijia.model.entity.coupon.CustomerCoupon;
import com.atguigu.daijia.model.form.coupon.UseCouponForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.coupon.AvailableCouponVo;
import com.atguigu.daijia.model.vo.coupon.NoReceiveCouponVo;
import com.atguigu.daijia.model.vo.coupon.NoUseCouponVo;
import com.atguigu.daijia.model.vo.coupon.UsedCouponVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {

    @Autowired
    private CouponInfoMapper couponInfoMapper;

    @Autowired
    private CustomerCouponMapper customerCouponMapper;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 查询未领取优惠券分页列表
     *
     * @param pageParam
     * @param customerId
     * @return
     */
    @Override
    public PageVo<NoReceiveCouponVo> findNoReceivePage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<NoReceiveCouponVo> pageInfo =
                couponInfoMapper.findNoReceivePage(pageParam, customerId);
        return new PageVo<>(pageInfo.getRecords(), pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 查询未使用优惠券分页列表
     *
     * @param pageParam
     * @param customerId
     * @return
     */
    @Override
    public PageVo<NoUseCouponVo> findNoUsePage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<NoUseCouponVo> pageInfo =
                couponInfoMapper.findNoUsePage(pageParam, customerId);
        return new PageVo<>(pageInfo.getRecords(), pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 查询已使用优惠券分页列表
     *
     * @param pageParam
     * @param customerId
     * @return
     */
    @Override
    public PageVo<UsedCouponVo> findUsedPage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<UsedCouponVo> pageInfo =
                couponInfoMapper.findUsedPage(pageParam, customerId);
        return new PageVo<>(pageInfo.getRecords(), pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 领取优惠券
     *
     * @param customerId
     * @param couponId
     * @return
     */
    @Override
    public Boolean receive(Long customerId, Long couponId) {
        //1、查询优惠券
        CouponInfo couponInfo = this.getById(couponId);
        if (couponInfo == null) {
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        //2、优惠券过期日期判断
        Date expireTime = couponInfo.getExpireTime();
        if (expireTime.before(new Date())) {
            throw new GuiguException(ResultCodeEnum.COUPON_EXPIRE);
        }
        //3、校验库存，优惠券领取数量判断
        if (couponInfo.getPublishCount() != 0 &&
                couponInfo.getPublishCount() == couponInfo.getReceiveCount()) {
            throw new GuiguException(ResultCodeEnum.COUPON_LESS);

        }
        RLock lock = redissonClient.getLock(RedisConstant.COUPON_LOCK + customerId);
        try {
            boolean tryLock = lock.tryLock(RedisConstant.COUPON_LOCK_WAIT_TIME,
                    RedisConstant.COUPON_LOCK_LEASE_TIME,
                    TimeUnit.SECONDS);
            if (tryLock) {
                //4、校验每人限领数量
                if (couponInfo.getPerLimit() > 0) {
                    //4.1、统计当前用户对当前优惠券的已经领取的数量
                    Long receivedCount = customerCouponMapper.selectCount(
                            new LambdaQueryWrapper<CustomerCoupon>()
                                    .eq(CustomerCoupon::getCouponId, couponId)
                                    .eq(CustomerCoupon::getCustomerId, customerId)
                    );
                    //4.2、校验限领数量
                    if (receivedCount >= couponInfo.getPerLimit()) {
                        throw new GuiguException(ResultCodeEnum.COUPON_USER_LIMIT);
                    }
                }
                //5、更新优惠券领取数量
                int rows = couponInfoMapper.update(couponInfo, new LambdaUpdateWrapper<CouponInfo>()
                        .set(couponInfo.getReceiveCount() < couponInfo.getPublishCount(), //乐观锁
                                CouponInfo::getReceiveCount, couponInfo.getReceiveCount() + 1)
                        .eq(CouponInfo::getId, couponId)
                );
                if (rows == 1) {
                    //6、保存领取记录
                    this.saveCustomerCoupon(customerId, couponId, couponInfo.getExpireTime());
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
        throw new GuiguException(ResultCodeEnum.COUPON_LESS);
    }

    private void saveCustomerCoupon(Long customerId, Long couponId, Date expireTime) {
        CustomerCoupon customerCoupon = new CustomerCoupon();
        customerCoupon.setCouponId(couponId);
        customerCoupon.setCustomerId(customerId);
        customerCoupon.setExpireTime(expireTime);
        customerCoupon.setStatus(1);
        customerCoupon.setReceiveTime(new Date());
        customerCouponMapper.insert(customerCoupon);
    }

    /**
     * 获取未使用的最佳优惠券信息
     *
     * @param customerId
     * @param orderAmount
     * @return
     */
    @Override
    public List<AvailableCouponVo> findAvailableCoupon(Long customerId, BigDecimal orderAmount) {
        List<AvailableCouponVo> list = new ArrayList<>();
        List<NoUseCouponVo> noUseCouponVoList = couponInfoMapper.findNoUseList(customerId);
        // 过滤出 现金券
        List<NoUseCouponVo> cashCouponVoList = noUseCouponVoList
                .stream()
                .filter(coupon -> coupon.getCouponType() == 1)
                .toList();
        //现金券
        for (NoUseCouponVo noUseCouponVo : cashCouponVoList) {
            //使用门槛判断
            //2.1.1.没门槛，订单金额必须大于优惠券减免金额
            //减免金额
            BigDecimal reduceAmount = noUseCouponVo.getAmount();
            if (noUseCouponVo.getConditionAmount().doubleValue() == 0 &&
                    orderAmount.subtract(reduceAmount).doubleValue() > 0) {
                list.add(this.buildBestNoUseCouponVo(noUseCouponVo, reduceAmount));
            }
            //2.1.2.有门槛，订单金额大于优惠券门槛金额
            if (noUseCouponVo.getConditionAmount().doubleValue() > 0 &&
                    orderAmount.subtract(noUseCouponVo.getConditionAmount()).doubleValue() > 0) {
                list.add(this.buildBestNoUseCouponVo(noUseCouponVo, reduceAmount));
            }
        }
        //折扣券
        List<NoUseCouponVo> discountCouponVoList = noUseCouponVoList
                .stream()
                .filter(coupon -> coupon.getCouponType() == 2)
                .toList();
        for (NoUseCouponVo noUseCouponVo : discountCouponVoList) {
            //使用门槛判断
            //订单折扣后金额
            BigDecimal discountOrderAmount = orderAmount.multiply(noUseCouponVo.getDiscount())
                    .divide(new BigDecimal("10")).setScale(2, RoundingMode.HALF_UP);
            //减免金额
            BigDecimal reduceAmount = orderAmount.subtract(discountOrderAmount);
            //订单优惠金额
            //2.2.1.没门槛
            if (noUseCouponVo.getConditionAmount().doubleValue() == 0) {
                list.add(this.buildBestNoUseCouponVo(noUseCouponVo, reduceAmount));
            }
            //2.2.2.有门槛，订单折扣后金额大于优惠券门槛金额
            if (noUseCouponVo.getConditionAmount().doubleValue() > 0 &&
                    discountOrderAmount.subtract(noUseCouponVo.getConditionAmount()).doubleValue() > 0) {
                list.add(this.buildBestNoUseCouponVo(noUseCouponVo, reduceAmount));
            }
        }
        //排序
        if (CollectionUtil.isNotEmpty(list)) {
            Collections.sort(list, new Comparator<AvailableCouponVo>() {
                @Override
                public int compare(AvailableCouponVo o1, AvailableCouponVo o2) {
                    return Double.compare(o1.getReduceAmount().doubleValue(), o2.getReduceAmount().doubleValue());
                }
            });
        }
        return list;
    }

    private AvailableCouponVo buildBestNoUseCouponVo(NoUseCouponVo noUseCouponVo,
                                                     BigDecimal reduceAmount) {
        AvailableCouponVo availableCouponVo = new AvailableCouponVo();
        BeanUtils.copyProperties(noUseCouponVo, availableCouponVo);
        availableCouponVo.setReduceAmount(reduceAmount);
        availableCouponVo.setCouponId(noUseCouponVo.getId());
        return availableCouponVo;
    }

    /**
     * 使用优惠券
     *
     * @param useCouponForm
     * @return
     */
    @Override
    public BigDecimal useCoupon(UseCouponForm useCouponForm) {
        //获取乘客优惠券
        CustomerCoupon customerCoupon =
                customerCouponMapper.selectById(useCouponForm.getCustomerCouponId());
        if (customerCoupon == null) {
            throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
        }
        //获取优惠券信息
        CouponInfo couponInfo = this.getById(customerCoupon.getCouponId());
        if (couponInfo == null) {
            throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
        }
        //判断该优惠券是否为乘客所有
        if (customerCoupon.getCustomerId().longValue() != useCouponForm.getCustomerId().longValue()) {
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        //获取优惠券减免金额
        BigDecimal reduceAmount = null;
        if (couponInfo.getCouponType() == 1) { //金额券
            //使用门槛判断
            //2.1.1.没门槛，订单金额必须大于优惠券减免金额
            if (couponInfo.getConditionAmount().doubleValue() == 0 &&
                    useCouponForm.getOrderAmount().subtract(couponInfo.getAmount()).doubleValue() > 0) {
                //减免金额
                reduceAmount = couponInfo.getAmount();
            }
            //2.1.2.有门槛，订单金额大于优惠券门槛金额
            if (couponInfo.getConditionAmount().doubleValue() > 0 &&
                    useCouponForm.getOrderAmount().subtract(couponInfo.getConditionAmount()).doubleValue() > 0) {
                //减免金额
                reduceAmount = couponInfo.getAmount();
            }
        }
        if (couponInfo.getCouponType() == 2) {
            //折扣券
            //折扣金额
            BigDecimal discountOrderAmount = useCouponForm.getOrderAmount()
                    .multiply(couponInfo.getDiscount())
                    .divide(new BigDecimal("10"))
                    .setScale(2, RoundingMode.HALF_UP);
            //使用门槛判断  没有门槛
            if (couponInfo.getConditionAmount().doubleValue() == 0) {
                //减免金额
                reduceAmount = useCouponForm.getOrderAmount().subtract(discountOrderAmount);
            }
            //有门槛
            if (couponInfo.getConditionAmount().doubleValue() > 0 &&
                    discountOrderAmount.subtract(couponInfo.getConditionAmount()).doubleValue() > 0) {
                //减免金额
                reduceAmount = useCouponForm.getOrderAmount().subtract(discountOrderAmount);
            }
        }

        if (reduceAmount != null && reduceAmount.doubleValue() > 0) {
            int row = couponInfoMapper.update(couponInfo, new LambdaUpdateWrapper<CouponInfo>()
                    .set(CouponInfo::getUseCount, couponInfo.getUseCount() + 1)
                    .eq(CouponInfo::getId, couponInfo.getId()));

            if (row == 1) {
                customerCouponMapper.update(customerCoupon,
                        new LambdaUpdateWrapper<CustomerCoupon>()
                                .set(CustomerCoupon::getStatus, 2)
                                .set(CustomerCoupon::getUsedTime, new Date())
                                .set(CustomerCoupon::getOrderId, useCouponForm.getOrderId())
                                .eq(CustomerCoupon::getCustomerId, useCouponForm.getCustomerId())
                                .eq(CustomerCoupon::getId, useCouponForm.getCustomerCouponId()));
                return reduceAmount;
            }
        }

        throw new GuiguException(ResultCodeEnum.DATA_ERROR);
    }
}
